
public class AgentIDAB implements Agent {

	private String role;
	private String opponentRole;
	private int playclock;
	private long startTime;
	private long timeLimit;
	private long timeCutoff;
	private int bestMove;
	private int bestValue = Integer.MIN_VALUE;
	private int bestDepth = Integer.MAX_VALUE;
	
	private boolean myTurn;
	private State currentState;
	private int stateExpansions;
	private boolean terminalReached;
	/*
		init(String role, int playclock) is called once before you have to select the first action. Use it to initialize the agent. role is either "WHITE" or "RED" and playclock is the number of seconds after which nextAction must return.
	*/
    public void init(String role, int playclock) {
		this.role = role;
		this.playclock = playclock * 1000; // convert seconds to milliseconds
		myTurn = !role.equals("WHITE");
		
		opponentRole = (role.equals("WHITE")) ? "RED" : "WHITE";

		currentState = new State(role);
		stateExpansions = 0;
		timeCutoff = 250; // How soon from the time limit that the algorithm will stop searching. 


    }

	// lastDrop is 0 for the first call of nextAction (no action has been executed),
	// otherwise it is a number n with 0<n<8 indicating the column that the last piece was dropped in by the player whose turn it was
    public String nextAction(int lastDrop) { 
		// TODO: 1. update your internal world model according to the action that was just executed
		if (lastDrop != 0)
		{
			currentState.makeMove(lastDrop-1);
		}

		myTurn = !myTurn;
		int drop = 0;
		if (myTurn)
		{
			currentState.print();
			
			drop = IDAlphaBetaSearch(currentState);
			
			System.out.println("Best move: " + bestMove + " - Value: " + bestValue);
			bestValue = -1000;

			System.out.println("State expansions this turn: " + stateExpansions + " - Time Taken: " + (System.currentTimeMillis() - startTime));
		}

		
		if (myTurn) {
			return "(DROP " + (drop+1) + ")";
		} else {
			return "NOOP";
		}
	}
    
    public int IDAlphaBetaSearch(State state)
    {
    	stateExpansions = 0;
    	int depth = 0, depthLimit = 1, alpha = Integer.MIN_VALUE, beta = Integer.MAX_VALUE, value;
    	startTime = System.currentTimeMillis();
    	timeLimit = startTime + playclock;
    	
    	while (System.currentTimeMillis() < timeLimit-timeCutoff)
    	{
	    	value = MiniMax(state, alpha, beta, depth, depthLimit, role);
	    	depthLimit++;
    	} 
    	return bestMove;
    }
    
    public int MiniMax(State state, int alpha, int beta, int depth, int depthLimit, String player)
    {
    	
    	if (state.terminalTest() || depth >= depthLimit || System.currentTimeMillis() > timeLimit-timeCutoff)
    	{
    		return state.evaluate(depthLimit);
    	}
    	
    	int value;
    	if (player.equals(role))
    	{
        	value = Integer.MIN_VALUE;
        	for (int i : state.legalMoves())
        	{
        		State successor = state.getSuccessor(i);
        		stateExpansions++;
        		value = Math.max(value, MiniMax(successor, alpha, beta, depth+1, depthLimit, opponentRole));
        		
        		if (value > bestValue)
        		{
        			bestValue = value;
        			bestMove = i;
        			bestDepth = depth;
        		}
        		
        		if (value >= beta)
        		{
        			return value;
        		}
        		
        		alpha = Math.max(alpha,  value);
        	}
    	}
    	else
    	{
        	value = Integer.MAX_VALUE;
        	for (int i : state.legalMoves())
        	{
        		State successor = state.getSuccessor(i);
        		stateExpansions++;
        		value = Math.min(value, MiniMax(successor, alpha, beta, depth+1, depthLimit, role));
        		if (value <= alpha)
        		{
        			return value;
        		}
        		beta = Math.min(beta, value);
        	}
    	}
    	return value;
    	
    }
	
}
